package com.lile.collection;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;

/*
 * Collection:
 * 		List:元素有序(存储和取出顺序一致)，可重复
 * 		Set:元素无序,唯一
 */
public class SetDemo {

	public static void main(String[] args) {
		// test1();
		// test2();
		// linkedHashSetTest();
		// treeSetTest();
		// treeSetTest1();
		test3();
	}

	/**
	 * HashSet：不保证 set 的迭代顺序，特别是它不保证该顺序恒久不变。
	 */
	public static void test1() {
		Set<String> set = new HashSet<String>();

		set.add("Hello");
		set.add("java");
		set.add("java");
		set.add("world");
		set.add("hei");

		for (String string : set) {
			System.out.println(string);
		}

		System.out.println("set = " + set);
	}

	/*
	 * 需求：存储自定义对象并遍历。(如果对象的成员变量值都相同，我们就认为这两个对象是同一个对象，也就是重复的元素)
	 * 我们存储完元素后，发现没有达到我们想要的效果。 接着我们重写了equals()方法，发现还是不行。 怎么办呢?我们就应该思考看哪里会出现问题?
	 * 通过简单的分析，我们知道肯定是add()那里出问题了 所以，我们要想知道出什么问题了,我们就应该看add()方法的源码
	 * 通过查看源码，我们看到了如下的一个判断： if (e.hash == hash && ((k = e.key) == key ||
	 * key.equals(k))){} A:e.hash == hash 比较的是对象的哈希值是否相同 B:((k = e.key) == key
	 * || key.equals(k)) 左边比较的是地址是否相同 右边比较的是内容是否相同
	 * HashSet集合底层数据结构是哈希表，哈希表由hashCode()和equals()方法来保证元素的唯一性。
	 * 比较对象的hashCode()值是否相同 是：继续比较equals()方法，看内容是否相同 返回true：就不添加到集合
	 * 返回false：就添加到集合 否：直接添加到集合
	 * 
	 * 学习完毕后，我们只要知道HashSet是如何保证元素的唯一性的。
	 * 在实际开发中，我们看到这种结构的集合，就要重写两个方法：hashCode()和equals()。 但是大家不用担心，因为这两个方法都是自动生成。
	 */
	public static void test2() {
		HashSet<Person> hs = new HashSet<Person>();

		// 创建元素对象
		Person p1 = new Person("吕洞宾", 30);
		Person p2 = new Person("何仙姑", 22);
		Person p3 = new Person("曹国舅", 25);
		Person p4 = new Person("铁拐李", 35);
		Person p5 = new Person("张果老", 33);
		Person p6 = new Person("蓝采和", 22);
		Person p7 = new Person("何仙姑", 22);
		Person p8 = new Person("铁拐李", 18);

		// 把元素添加到集合
		hs.add(p1);
		hs.add(p1);
		hs.add(p2);
		hs.add(p3);
		hs.add(p4);
		hs.add(p5);
		hs.add(p6);
		hs.add(p7);
		hs.add(p8);

		// 遍历集合
		for (Person p : hs) {
			System.out.println(p.getName() + "---" + p.getAge());
		}
	}

	/*
	 * LinkedHashSet：底层数据结构是哈希表和链表。 哈希表：能够保证元素的唯一性 链表：能够保证元素有序
	 */
	public static void linkedHashSetTest() {
		LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>();
		linkedHashSet.add("ccc");
		linkedHashSet.add("hello");
		linkedHashSet.add("bbb");
		linkedHashSet.add("world");
		linkedHashSet.add("aaa");
		linkedHashSet.add("java");
		linkedHashSet.add("aaa");
		linkedHashSet.add("java");

		System.out.println("linkedHashSet:" + linkedHashSet);
	}

	/*
	 * TreeSet:底层数据结构是二叉树。
	 * 可以让元素排序，有两种方案，分别是：自然排序，以及Comparator(比较器)进行排序,具体使用哪种情况看你使用的构造方法。
	 */
	public static void treeSetTest() {
		TreeSet<Integer> ts = new TreeSet<Integer>();

		ts.add(20);
		ts.add(18);
		ts.add(23);
		ts.add(22);
		ts.add(17);
		ts.add(24);
		ts.add(19);
		ts.add(18);
		ts.add(24);

		for (Integer integer : ts) {
			System.out.println(integer);
		}
	}

	/*
	 * TreeSet是如何保证元素排序的呢?又是如何保证元素唯一的呢?
	 * 
	 * 需求：用TreeSet存储自定义对象，并按照要求排序和去掉重复值。 a:如果成员变量值都相同，我就认为是重复元素 b:请按照年龄的从小到大进行排序
	 * 
	 * 通过观察的现象，我们大体明白了这样的一个意思： 从第二个元素开始，在添加的时候，会和前面的元素进行比较，走的是compareTo()方法
	 * 如果返回的是正数：大，往后放 如果返回的是负数：小，往前放 如果返回的是0：说明重复。不放。
	 * 
	 * 总结：TreeSet保证元素唯一和排序 唯一性：根据比较的返回值是否是0来判断元素是否是唯一的 排序：两种方式 A:自然排序 (元素具备比较性)
	 * 让对象所属的类实现Comparable接口 B:比较器排序 (集合具备比较性)
	 * 创建集合对象的时候，接受Comparator接口的实现类对象。一般我们使用的是匿名内部类实现。
	 * 
	 */
	public static void treeSetTest1() {

		// TreeSet<Person> ts = new TreeSet<Person>(new Comparator<Person>() {
		//
		// @Override
		// public int compare(Person p1, Person p2) {
		// int num = p1.getAge() - p2.getAge();
		// int num2 = num == 0 ? p1.getName().compareTo(p2.getName())
		// : num;
		// return num2;
		// }
		// });

		TreeSet<Person> ts = new TreeSet<Person>();

		// 创建人对象
		Person p1 = new Person("tangseng", 30);
		Person p2 = new Person("jiapingwa", 25);
		Person p3 = new Person("zhangyimou", 25);
		Person p4 = new Person("xijinping", 26);
		Person p5 = new Person("yanni", 27);
		Person p6 = new Person("wenzhang", 30);
		Person p7 = new Person("tianliang", 28);
		Person p8 = new Person("xijinping", 26);

		// 添加元素
		ts.add(p1);
		ts.add(p2);
		ts.add(p3);
		ts.add(p4);
		ts.add(p5);
		ts.add(p6);
		ts.add(p7);
		ts.add(p8);

		// 遍历集合
		for (Person p : ts) {
			System.out.println(p.getName() + "---" + p.getAge());
		}
	}

	/*
	 * Set集合我们讲解了两个子类。一般我们使用谁呢? 要排序吗? 要：TreeSet 不要：HashSet
	 * 我们也不知道是不是要排序，用HashSet。
	 * 
	 * 编写一个程序，获取10个1至20的随机数，要求随机数不能重复。
	 * 
	 * 分析： A:定义HashSet集合，存储Integer类型的数据 B:直接判断集合的长度 小于10，继续产生随机数，存储 C:遍历集合
	 */
	public static void test3() {
		HashSet<Integer> hs = new HashSet<Integer>();

		while (hs.size() < 10) {
			int number = (int) (Math.random() * 20) + 1;
			hs.add(number);
		}

		for (Integer integer : hs) {
			System.out.println(integer);
		}
	}

}
